package org.hamster.common.manager.pointcut
{
	import flash.events.EventDispatcher;
	
	import mx.collections.ArrayCollection;
	
	import org.hamster.common.manager.pointcut.events.PointcutEvent;
	import org.hamster.common.manager.pointcut.vo.PointcutVO;
	
	import spark.collections.Sort;
	import spark.collections.SortField;

	[Event(type="org.hamster.common.manager.pointcut.events.PointcutEvent", name="pointcutFinish")]
	[Event(type="org.hamster.common.manager.pointcut.events.PointcutEvent", name="pointcutCancel")]
	
	public class PointcutManager extends EventDispatcher
	{
		public function PointcutManager()
		{
		}
		
		private const _pointcutList:ArrayCollection = new ArrayCollection();
		public function get pointcutList():ArrayCollection
		{
			return _pointcutList;
		}
		
		public var data:Object;
		
		private static const POINTCUT_SORT:Sort = new Sort();
		
		public function clone():PointcutManager
		{
			var pointcutManager:PointcutManager = new PointcutManager();
			pointcutManager.pointcutList.addAll(this._pointcutList);
			return pointcutManager;
		}
		
		public function addPointcut(notificationName:String, timing:String, handler:Function, asyncTarget:IPointcutAsyncTarget = null, priority:int = 0):void
		{
			var pointcut:PointcutVO = new PointcutVO(notificationName, timing, handler, asyncTarget, priority);
			var isAdded:Boolean = false;
			for each (var p:PointcutVO in this._pointcutList) {
				if (p.priority >= priority) {
					continue;
				} else {
					this._pointcutList.addItem(pointcut);
					isAdded = true;
				}
			}
			
			if (!isAdded) {
				this._pointcutList.addItem(pointcut);
			}
		}
		
		public function removePointcut(notificationName:String, timing:String, handler:Function, asyncTarget:IPointcutAsyncTarget = null):void
		{
			for each (var pointcut:PointcutVO in _pointcutList) {
				if (pointcut.notification == notificationName && pointcut.timing == timing 
					&& pointcut.handler == handler && pointcut.asyncTarget == asyncTarget) {
					_pointcutList.removeItemAt(_pointcutList.getItemIndex(pointcut));
				}
			}
		}
		
		public function hasPointcut(notificationName:String, timing:String):Boolean
		{
			for each (var pointcut:PointcutVO in _pointcutList) {
				if (pointcut.notification == notificationName && pointcut.timing == timing) {
					return true;
				}
			}
			return false;
		}
		
		public function getPointcut(notificationName:String, timing:String):Array
		{
			var result:Array = [];
			for each (var pointcut:PointcutVO in _pointcutList) {
				result.push(pointcut);
			}
			
			return result;
		}
		
		public function executePointcuts(notificationName:String, timing:String):void
		{
			executeNext(notificationName, timing);
		}
		
		private function executeNext(notificationName:String, timing:String, currentPointcutVO:PointcutVO = null):void
		{
			var array:Array = this.getPointcut(notificationName, timing);
			var isAsync:Boolean = false;
			var pointcutVO:PointcutVO;
			var toPointcutVO:PointcutVO;
			var disEvt:PointcutEvent;
			var isFound:Boolean = currentPointcutVO == null;
			

			var nextIndex:int = currentPointcutVO == null ? 0 : array.indexOf(currentPointcutVO) + 1;
			for (var i:int = 0; i < array.length; i++) {
				if (nextIndex > i) {
					continue;
				}
				
				pointcutVO = array[i];
				if (pointcutVO.asyncTarget == null) {
					nextIndex++;
					var result:Boolean = this.executePointcut(pointcutVO);
					if (result == false) {
						disEvt = new PointcutEvent(PointcutEvent.POINTCUT_CANCEL, pointcutVO);
						this.dispatchEvent(disEvt);
						return;
					}
				} else {
					isAsync = true;
					this.executePointcut(pointcutVO);
					return;
				}
			}
			
			
//			for each (pointcutVO in array) {
//				if (!isFound && currentPointcutVO != null && currentPointcutVO == pointcutVO) {
//					isFound = true;
//					continue;
//				} else if (!isFound) {
//					continue;
//				} else if (isFound && toPointcutVO == null) {
//					toPointcutVO = pointcutVO;
//				}
//				
//				
//				if (pointcutVO.asyncTarget == null) {
//					toPointcutVO = null;
//					isFound      = false;
//					var result:Boolean = this.executePointcut(pointcutVO);
//					if (result == false) {
//						disEvt = new PointcutEvent(PointcutEvent.POINTCUT_CANCEL);
//						this.dispatchEvent(disEvt);
//						return;
//					}
//				} else {
//					isAsync = true;
//					this.executePointcut(pointcutVO);
//					break;
//				}
//			}
			
			if (isAsync == false) {
				disEvt = new PointcutEvent(PointcutEvent.POINTCUT_FINISH, null);
				this.dispatchEvent(disEvt);
			}
		}
		
		private function executePointcut(pointcutVO:PointcutVO):*
		{
			if (pointcutVO.asyncTarget) {
				pointcutVO.addEventListener(PointcutEvent.POINTCUT_NEXT,   pointcutNextHandler);
				pointcutVO.addEventListener(PointcutEvent.POINTCUT_CANCEL, pointcutCancelHandler);
				pointcutVO.asyncTarget.execute(pointcutVO);
				return null;
			} else {
				return pointcutVO.handler.apply(null, [pointcutVO]);
			}
		}
		
		public function cleanPointcuts():void
		{
			this._pointcutList.removeAll();
		}
		
		
		private function pointcutNextHandler(event:PointcutEvent):void
		{
			var fromPointcutVO:PointcutVO = event.currentTarget as PointcutVO;
			fromPointcutVO.removeEventListener(PointcutEvent.POINTCUT_NEXT, pointcutNextHandler);
			fromPointcutVO.removeEventListener(PointcutEvent.POINTCUT_CANCEL, pointcutCancelHandler);
			this.executeNext(fromPointcutVO.notification, fromPointcutVO.timing, fromPointcutVO);
		}
		
		private function pointcutCancelHandler(event:PointcutEvent):void
		{
			var fromPointcutVO:PointcutVO = event.currentTarget as PointcutVO;
			fromPointcutVO.removeEventListener(PointcutEvent.POINTCUT_NEXT, pointcutNextHandler);
			fromPointcutVO.removeEventListener(PointcutEvent.POINTCUT_CANCEL, pointcutCancelHandler);
			this.dispatchEvent(event);
		}
	}
}